package com.dwy2002.common.aspect;

import com.dwy2002.common.annotation.RequiresPermission;
import com.dwy2002.common.exception.AccessDeniedException;
import com.dwy2002.common.utils.JwtUtil;
import com.dwy2002.enums.Logical;
import com.dwy2002.pojo.vo.users.UserVO;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;

import static com.dwy2002.common.utils.Utils.getToken;

/**
 * 权限校验切面
 *
 * @author 杜伟毅
 * @version 1.0
 * @since 2025/04/03
 */
@Aspect
@Component
@RequiredArgsConstructor
public class PermissionAspect {
    private final JwtUtil jwtUtil;

    @Around("@annotation(requiresPermission)")
    public Object checkPermission(ProceedingJoinPoint joinPoint, RequiresPermission requiresPermission) throws Throwable {

        // 获取目标方法对象
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        // 获取方法名和类名
        String methodName = method.getName();
        String className = method.getDeclaringClass().getSimpleName();

        // 1. 查询用户所有权限代码
        UserVO userVO = jwtUtil.parseToken(getToken(), UserVO.class);
        List<String> userPerms = userVO.getRole().getPermCodes();

        // 2. 校验权限
        boolean hasPerm;
        String[] requiredPerms = requiresPermission.value();
        if (requiresPermission.logical() == Logical.AND) {
            hasPerm = Arrays.stream(requiredPerms).allMatch(userPerms::contains);
        } else {
            hasPerm = Arrays.stream(requiredPerms).anyMatch(userPerms::contains);
        }

        if (!hasPerm) {
            // 将方法信息添加到异常消息中
            String errorMsg = String.format("用户ID：[%s],拒绝访问方法 [%s.%s]", userVO.getId(), className, methodName);

            throw new AccessDeniedException(errorMsg);
        }

        return joinPoint.proceed();
    }
}
